package seismosurfer.gui;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import seismosurfer.data.constants.ParameterNames;
import seismosurfer.gui.menu.QueryMenu;
import seismosurfer.layer.QuakeLayer;
import seismosurfer.util.Assert;
import seismosurfer.util.ClientRegistry;
import seismosurfer.util.TimeUtil;

import com.bbn.openmap.LightMapHandlerChild;
import com.bbn.openmap.MapBean;
import com.bbn.openmap.gui.BasicMapPanel;
import com.bbn.openmap.proj.Projection;
import com.bbn.openmap.util.Debug;

/**
 * An extension to JSlider, this component is used to
 * watch earthquake activity over time in a map area.
 * Implements the PropertyChangeListener, LightMapHandlerChild
 * interfaces to make it fit with the OpenMap architecture.
 *
 */
public class TimeSlider extends JSlider implements PropertyChangeListener,
        LightMapHandlerChild, ParameterNames {
    
    private static final long serialVersionUID = 4399037117449744551L;

    public static final String DEBUG = "TimeSlider";

    public static final String DAY = "Day";

    public static final String MONTH = "Month";

    public static final String YEAR = "Year";

    private QuakeLayer ql = null;

    private ChangeListener cl;

    protected double latMin;

    protected double latMax;

    protected double longMin;

    protected double longMax;

    protected MapBean map;

    private String interval;

    double magMin, magMax;

    int yearMin, monthMin;

    boolean runQuery = false;

    public TimeSlider() {
        super();
        addChangeListener(getChangeListener());
    }
    
    /**
     * Creates a ChangeListener if needed.
     * The ChangeListener implementation used here is
     * TimeSliderChangeListener.
     * 
     * @return a ChangeListener object
     */
    public ChangeListener getChangeListener() {
        if (cl == null) {
            cl = new TimeSliderChangeListener();
        }
        return cl;
    }

    public void findAndInit(Object someObj) {

        if (someObj instanceof QuakeLayer) {
            if (Debug.debugging(DEBUG)) {
                Debug.output("QuakeLayer found!!!");
            }
            ql = (QuakeLayer) someObj;

        }

        if (someObj instanceof BasicMapPanel) {
            ((BasicMapPanel) someObj).addMapComponent(this);
        }

    }

    public void findAndUndo(Object someObj) {
    }
    
    /**
     * Updates the min/max values of the latitude and longitude
     * of the current projection.
     *
     */
    protected void setCoordinates() {
        if (map == null) {
            map = ClientRegistry.getMapBean();
        }
        Projection p = map.getProjection();

        if (Debug.debugging(DEBUG)) {
            Debug.output("Set coordinates:" + p.toString());
        }

        latMin = p.getLowerRight().getLatitude();
        latMax = p.getUpperLeft().getLatitude();
        longMin = p.getUpperLeft().getLongitude();
        longMax = p.getLowerRight().getLongitude();
    }

    public void setMagMin(double magMin) {
        this.magMin = magMin;
    }

    public void setMagMax(double magMax) {
        this.magMax = magMax;
    }

    public void setYearMin(int yearMin) {
        this.yearMin = yearMin;
    }

    public void setMonthMin(int monthMin) {
        this.monthMin = monthMin;
    }

    public void setRunQuery(boolean runQuery) {
        this.runQuery = runQuery;
    }

    public boolean canRunQuery() {
        return runQuery;
    }

    public void setTimeResolution(String interval) {
        this.interval = interval;
    }

    public String getTimeResolution() {
        return interval;
    }
    
    /**
     * A ChangeListener implementation that monitors
     * the slider for changes and updates 
     * accordingly the map with new objects.
     *
     */
    protected class TimeSliderChangeListener implements ChangeListener {

        public void stateChanged(ChangeEvent e) {
            if (!canRunQuery()) {
                return;
            }

            JSlider slider = (JSlider) e.getSource();
            if (!slider.getValueIsAdjusting()) {
                setCoordinates();
                sendQueryParameters();
            }
        }
    }

    private Calendar minCal = Calendar.getInstance();

    private Calendar maxCal = Calendar.getInstance();
    
    /**
     * Sets the parameters for a TIME query
     * and updates the QuakeLayer with the 
     * new data.
     *
     */
    public void sendQueryParameters() {

        setCoordinates();

        int selectedValue = this.getValue();

        minCal.clear();
        maxCal.clear();

        if (interval.equals(YEAR)) {

            TimeUtil.setYear(minCal, selectedValue);
            TimeUtil.setYear(maxCal, selectedValue);

            minCal.set(Calendar.MONTH, 0);
            maxCal.set(Calendar.MONTH, 11);
            minCal.set(Calendar.DAY_OF_MONTH, 1);
            maxCal.set(Calendar.DAY_OF_MONTH, 31);
        } else if (interval.equals(MONTH)) {
            TimeUtil.setYear(minCal, yearMin);
            TimeUtil.setYear(maxCal, yearMin);

            minCal.set(Calendar.MONTH, selectedValue - 1);
            maxCal.set(Calendar.MONTH, selectedValue - 1);
            minCal.set(Calendar.DAY_OF_MONTH, 1);
            maxCal.set(Calendar.DAY_OF_MONTH, 31);
        } else {
            TimeUtil.setYear(minCal, yearMin);
            TimeUtil.setYear(maxCal, yearMin);

            minCal.set(Calendar.MONTH, monthMin - 1);
            maxCal.set(Calendar.MONTH, monthMin - 1);

            minCal.set(Calendar.DAY_OF_MONTH, selectedValue);
            maxCal.set(Calendar.DAY_OF_MONTH, selectedValue);
            minCal.set(Calendar.HOUR_OF_DAY, 0);
            maxCal.set(Calendar.HOUR_OF_DAY, 24);
        }

        Map parameters = new HashMap();

        parameters.put(TIME_MIN, new Long(minCal.getTimeInMillis()));
        parameters.put(TIME_MAX, new Long(maxCal.getTimeInMillis()));

        parameters.put(MAG_MIN, new Double(magMin));
        parameters.put(MAG_MAX, new Double(magMax));

        parameters.put(LONG_MIN, new Double(longMin));
        parameters.put(LONG_MAX, new Double(longMax));

        parameters.put(LAT_MIN, new Double(latMin));
        parameters.put(LAT_MAX, new Double(latMax));

        parameters.put(SOURCE, getSource());

        if (Debug.debugging(DEBUG)) {
            Debug.output("\nTimeSlider parameters:" + parameters);
        }

        ql.loadQuakeClientData("TIME", parameters);

    }

    public void propertyChange(PropertyChangeEvent e) {
        if (e.getPropertyName().equalsIgnoreCase(QueryMenu.SourceProperty)) {
            setSource((String) e.getNewValue());
            if (Debug.debugging(DEBUG)) {
                Debug.output("Source property changed:" + getSource());
            }
        }
    }

    public String getSource() {
        return source;
    }

    public void setSource(String source) {
        Assert.notNull(source);
        this.source = source;
    }

    private String source;

}
